package emailserving.component;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

@Component
public class EmailTaskPool {
    private static final Logger LOG = LoggerFactory.getLogger(EmailTaskPool.class);

    @Autowired
    ExecutorService executorService;

    private Map<Long, List<EmailTask>> jobTasksMap = Maps.newConcurrentMap();

    public boolean existsExecutingTask(Long jobId) {
        Preconditions.checkNotNull(jobId);
        return existsTasks(jobId) && getAliveTasksCount(jobId) > 0;
    }

    public boolean existsTasks(Long jobId) {
        Preconditions.checkNotNull(jobId);
        return jobTasksMap.containsKey(jobId) && jobTasksMap.get(jobId) != null && !jobTasksMap.get(jobId).isEmpty();
    }


    public EmailTaskPool registerTasksForJob(Long jobId, List<EmailTask> tasks) {
        Preconditions.checkNotNull(tasks, "Tasks of " + jobId + " couldn't be null");
        if (!existsTasks(jobId)) {
            jobTasksMap.put(jobId,
                    tasks.stream().filter(t -> t != null).collect(Collectors.toList()));
        }
        return this;
    }

    public void start(long jobId) {
        if (existsTasks(jobId)) {
            jobTasksMap.get(jobId).stream().filter(emailTask -> emailTask.getState() == Thread.State.NEW)
                    .forEach(Thread::start);
            LOG.info("start job: " + jobId);
        }
    }

    public void stop(long jobId) {
        if (existsTasks(jobId)) {
            jobTasksMap.get(jobId).stream().forEach(EmailTask::manualStop);
            jobTasksMap.remove(jobId);
            LOG.info("stop job: " + jobId);
        }
    }


    public long getAliveTasksCount(Long jobId) {
        Preconditions.checkNotNull(jobId);
        if (existsTasks(jobId)) {
            return jobTasksMap.get(jobId).stream().filter(Thread::isAlive).count();
        } else {
            return 0;
        }
    }

    public long getDeadTasksCount(Long jobId) {
        Preconditions.checkNotNull(jobId);
        if (existsTasks(jobId)) {
            return jobTasksMap.get(jobId).stream().filter(Thread::isInterrupted).count();
        } else {
            return 0;
        }
    }

    public long getTasksCount(Long jobId) {
        Preconditions.checkNotNull(jobId);
        if (existsTasks(jobId)) {
            return jobTasksMap.get(jobId).size();
        } else {
            return 0;
        }
    }


    public JobExecutingStatus executingStatus(Long jobId) {
        return new JobExecutingStatus(jobId, getTasksCount(jobId), getAliveTasksCount(jobId));
    }

    public List<JobExecutingStatus> executingStatus(List<Long> jobIds) {
        Preconditions.checkNotNull(jobIds);
        return jobIds.stream().filter(id -> id != null)
                .map(this::executingStatus)
                .collect(Collectors.toList());

    }


    public static class JobExecutingStatus {
        private long jobId;
        private long tasksTotalCount;
        private long aliveTasksCount;


        public JobExecutingStatus(long jobId, long tasksTotalCount, long aliveTasksCount) {
            this.jobId = jobId;
            this.tasksTotalCount = tasksTotalCount;
            this.aliveTasksCount = aliveTasksCount;
        }

        public long getJobId() {
            return jobId;
        }

        public long getTasksTotalCount() {
            return tasksTotalCount;
        }

        public long getAliveTasksCount() {
            return aliveTasksCount;
        }
    }


}
